#include"../include/img.h"

struct LCD_info *init_lcd(void){
    struct LCD_info *lcd_info = malloc(sizeof(struct LCD_info));
    lcd_info->fd = open("/dev/fb0", O_RDWR);
    if(lcd_info->fd < 0){
        perror("lcd打开失败");
        close(lcd_info->fd);
        return NULL;
    }
    struct fb_var_screeninfo vinfo;
    ioctl(lcd_info->fd, FBIOGET_VSCREENINFO, &vinfo);
    lcd_info->width = vinfo.xres;
    lcd_info->height = vinfo.yres;
    lcd_info->components = vinfo.bits_per_pixel / 8;
    lcd_info->screen_size = lcd_info->width * lcd_info->height * lcd_info->components;
    lcd_info->fb_mem = mmap(NULL, lcd_info->screen_size, PROT_WRITE, MAP_SHARED, lcd_info->fd, 0);
    bzero(lcd_info->fb_mem, lcd_info->screen_size);
    return lcd_info;
}

struct jpeg_info *jpg2rgb(const char *filename){
    struct jpeg_info *jinfo = malloc(sizeof(struct jpeg_info));
    //获取jpg文件名
    jinfo->filename = filename;
    //获取jpg文件大小
    struct stat st;
    bzero(&st, sizeof(st));//不熟练
    stat(filename, &st);
    jinfo->jpg_size = st.st_size;
    //打开jpg图片
    FILE *fp = fopen(filename, "r");
    if(!fp){
        printf("打开%s失败:%s\n", filename, strerror(errno));
        return NULL;
    }
    //读jpg图片信息
    //对fread函数不熟悉，忘记了返回值是成功读取数据块的大小、参数类型
    jinfo->jpg_data = malloc(jinfo->jpg_size);
    int n = fread(jinfo->jpg_data, jinfo->jpg_size, 1, fp);
    if(n < 1){
        perror("读文件失败");
        return NULL;
    }
    //新知识：主要难点、记忆点
    struct jpeg_decompress_struct cinfo;// 解压缩核心结构体
    struct jpeg_error_mgr jerr;// 错误管理结构体
    cinfo.err = jpeg_std_error(&jerr);//将错误管理结构体jerr与解压缩结构体dcp_info关联
    jpeg_create_decompress(&cinfo);//初始化解压缩结构体
    //配置解压缩结构体，使其从jpg_data中读取jpg_size个字节
    jpeg_mem_src(&cinfo, jinfo->jpg_data, jinfo->jpg_size);
    //读jpeg格式头, 判断其格式是否合法
    if(!jpeg_read_header(&cinfo, true)){//true表示要求 libjpeg 严格验证 JPEG 格式
        perror("jpeg_read_header failed!");
        return NULL;
    }
    //开始解码
    jpeg_start_decompress(&cinfo);
    //保存并显示图片的尺寸信息
    printf("图片规格:%d × %d\n", jinfo->width = cinfo.output_width, jinfo->height = cinfo.output_height);
    printf("色深:%d\n", 8 * (jinfo->components = cinfo.output_components));
    //根据图片大小分配一块放rgb_data的内存,大小需根据解码数据计算
    jinfo->rgb_size = jinfo->width * jinfo->components * jinfo->height;
    jinfo->rgb_data = calloc(1, jinfo->rgb_size);
    //循环地将解码出地每一行数据放入rgb_data地每一行中
    while(cinfo.output_scanline < jinfo->height){
        unsigned char *buffer_array[1];
        buffer_array[0] = jinfo->rgb_data + cinfo.output_scanline * jinfo->width * jinfo->components;
        jpeg_read_scanlines(&cinfo, buffer_array, 1);
    }
    //释放相关资源
    jpeg_finish_decompress(&cinfo);
    jpeg_destroy_decompress(&cinfo);
    fclose(fp);
    return jinfo;
}

void display_img(const char *file_name, uint8_t mode){
    struct LCD_info *lcd = init_lcd();
    struct jpeg_info *jinfo = jpg2rgb(file_name);
    bool zoom;
    zoom = (jinfo->width > lcd->width || jinfo->height > lcd->height);
    zoom = zoom && (mode & ZOOM);
    printf("zoom:%s\n", zoom ? "True":"False");

    bool center;
    center = (jinfo->width < lcd->width || jinfo->height < lcd->height);
    center = center && (mode & CENTER);
    printf("center:%s\n", center ? "True":"False");

    int count = 1;
    while(zoom && (jinfo->width/count > lcd->width || jinfo->height/count > lcd->height))
        count++;
    int x = (lcd->width - jinfo->width/count)/2;
    int y = (lcd->height - jinfo->height/count)/2;
    for(int i = y, k = 0; i < lcd->height-y && k < jinfo->height; i++, zoom?(k+=count):k++){
        int lcd_offset = lcd->width * lcd->components;
        int rgb_offset = jinfo->width * jinfo->components;
        for(int j = x, m = 0; j < lcd->width-x && m < jinfo->width; j++, zoom?(m+=count):m++){
            memcpy(lcd->fb_mem + j * lcd->components + i * lcd_offset + 0, jinfo->rgb_data + m * jinfo->components + k * rgb_offset + 2, 1);
            memcpy(lcd->fb_mem + j * lcd->components + i * lcd_offset + 1, jinfo->rgb_data + m * jinfo->components + k * rgb_offset + 1, 1);
            memcpy(lcd->fb_mem + j * lcd->components + i * lcd_offset + 2, jinfo->rgb_data + m * jinfo->components + k * rgb_offset + 0, 1);
        }
    }
    free(jinfo);
    munmap(lcd->fb_mem, lcd->screen_size);
    close(lcd->fd);
}